use crate::api::ApiClient;
use crate::components::{TodoInput, TodoItemsComponent};
use crate::errors::AppError;
use crate::models::{NewTodoItem, TodoInputItem, TodoItem, UpdateTodoItem};
use crate::utils::get_query_param;
//use gloo::console;
use std::cmp::Ordering;
use std::ops::Deref;
use wasm_bindgen_futures::spawn_local;
use yew::prelude::*;

async fn load_todos(owner_key: &str) -> Result<Vec<TodoItem>, AppError> {
    let mut result = ApiClient::new()?.get_todos(owner_key).await?;
    result.sort_by(|b, a| {
        match (b.finished, a.finished) {
            (false, true) => Ordering::Less,
            (true, false) => Ordering::Greater,
            _ => {
                // 如果finished相同，则根据title字段排序
                a.title.cmp(&b.title)
            }
        }
    });
    Ok(result)
}
async fn add_todo(todo_input: TodoInputItem, owner_key: &str) -> Result<(), AppError> {
    ApiClient::new()?
        .new_todo(&NewTodoItem {
            title: todo_input.title,
            description: todo_input.description,
            owner_key: owner_key.to_string(),
        })
        .await?;
    Ok(())
}
async fn update_todo(update_todo: UpdateTodoItem) -> Result<(), AppError> {
    ApiClient::new()?.update_todo(&update_todo).await?;
    Ok(())
}
const DEFAULT_OWNER_KEY: &str = "allinall";
#[function_component(Root)]
fn root() -> Html {
    let todos = use_state(Vec::<TodoItem>::new);
    let owner_key = use_state(|| "".to_string());
    let todo_text = use_state(|| "".to_string());
    let edit_todo = use_state::<Option<TodoItem>, _>(|| None);

    {
        let owner_key = owner_key.clone();
        use_effect_with((), move |_| {
            if let Some(key) = get_query_param("owner_key") {
                owner_key.set(key);
            } else {
                owner_key.set(DEFAULT_OWNER_KEY.to_string());
            }
        });
    }

    {
        let todos = todos.clone();
        use_effect_with(owner_key.clone(), move |owner_key_val| {
            let owner_key_val = owner_key_val.clone();
            if !owner_key_val.is_empty() {
                spawn_local(async move {
                    match load_todos(&owner_key_val).await {
                        Ok(result) => {
                            todos.set(result);
                        }
                        Err(err) => {
                            err.report("load_todos");
                        }
                    }
                })
            }
        });
    }

    let on_edit_click = {
        let todo_text = todo_text.clone();
        let edit_todo = edit_todo.clone();
        use_callback(todos.clone(), move |id, todo_items| {
            if let Some(todo) = todo_items.iter().find(|todo| todo.id == id) {
                todo_text.set(String::from(TodoInputItem::from(todo.clone())));
                edit_todo.set(Some(todo.clone()));
            }
        })
    };

    let handle_item_status_change =
        use_callback(todos.clone(), move |(id, new_status), todo_items| {
            if let Some(todo) = todo_items.iter().find(|todo| todo.id == id) {
                let todo = todo.clone();
                spawn_local(async move {
                    if let Err(err) = update_todo(UpdateTodoItem {
                        finished: new_status,
                        ..todo.into()
                    })
                    .await
                    {
                        err.report("update_todo with stauts");
                    }
                });
            }
        });
    let handle_add_todo = {
        let todo_text = todo_text.clone();
        let todos = todos.clone();
        use_callback(
            (owner_key.clone(), edit_todo.clone()),
            move |todo: String, (owner_key_val, edit_todo_val)| {
                let todo_input: TodoInputItem = (&todo).into();
                let owner_key_val = owner_key_val.clone();
                let edit_todo_val1 = edit_todo_val.deref().clone();
                let todos = todos.clone();

                edit_todo_val.set(None);
                todo_text.set("".to_string());
                if let Some(todo_item) = edit_todo_val1 {
                    spawn_local(async move {
                        if let Err(err) = update_todo(UpdateTodoItem {
                            title: todo_input.title,
                            description: todo_input.description,
                            ..todo_item.clone().into()
                        })
                        .await
                        {
                            err.report("update_todo");
                        }

                        match load_todos(&owner_key_val).await {
                            Ok(result) => {
                                todos.set(result);
                            }
                            Err(err) => {
                                err.report("load_todos");
                            }
                        }
                    });
                } else {
                    spawn_local(async move {
                        if let Err(err) = add_todo(todo_input, &owner_key_val).await {
                            err.report("add_todo");
                        }

                        match load_todos(&owner_key_val).await {
                            Ok(result) => {
                                todos.set(result);
                            }
                            Err(err) => {
                                err.report("load_todos");
                            }
                        }
                    });
                }
            },
        )
    };
    {
        let todo_items = todos.deref().clone();
        let todo_text = todo_text.deref().clone();

        html! {
            <div class={classes!("column", "align-center", "content-center", "w-100")}>
                <TodoInput initial_value={todo_text} on_add_todo={handle_add_todo}/>
                <TodoItemsComponent data={todo_items} on_status_changed={handle_item_status_change}
                    on_edit_click={on_edit_click}
                />
            </div>
        }
    }
}

#[function_component(App)]
pub fn app() -> Html {
    html! {
        <Root/>
    }
}
